﻿using M3u8Puller.Config;
using M3u8Puller.Kit;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Security.Policy;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Windows.Forms;
using System.Net.Security;
using System.Security.Authentication;
using System.Security.Cryptography.X509Certificates;
using System.Web.UI.WebControls.WebParts;
using M3u8Puller.Helper;

namespace M3u8Puller.Entity
{
    [Serializable]
    public class M3u8TaskEntity
    {
        public M3u8TaskEntity()
        {
            //this.Id = SeqKit.Next();
        }

        public long Id { get; set; }
        /// <summary>
        /// 0等待 1下载 2暂停 -1删除 3资源加载中 4下载失败 5下载完成 6读取视频中 7转换视频中
        /// </summary>
        public Int32 Status { get; set; }
        public string Name { get; set; }
        public string Url { get; set; }
        public List<TsEntity> Parts { get; set; }
        public int CompleteNum { get; set; }
        public int PartNum { get; set; }
        /// <summary>
        /// 加密key
        /// </summary>
        public string AESKey { get; set; }

        public string StatusName
        {
            get
            {
                var str = string.Empty;
                if (this.Status == 0)
                    str = "等待下载";
                else if (this.Status == 1)
                    str = "下载中";
                else if (this.Status == 2)
                    str = "暂停下载";
                else if (this.Status == 3)
                    str = "资源加载中";
                else if (this.Status == 4)
                    str = "下载失败";
                else if (this.Status == 5)
                    str = "下载完成";
                else if (this.Status == 6)
                    str = "读取视频中";
                else if (this.Status == 7)
                    str = "转换视频中";
                else
                    str = "无效资源";
                return str;
            }
        }

        /// <summary>
        /// 开始加载m3u8资源信息
        /// </summary>
        public void M3u8TaskStart()
        {
            if (Parts == null || Parts.Count == 0)
            {
                this.Parts = new List<TsEntity>();

                string m3u8Content = GetM3u8Content(Url);
                string[] attrs = m3u8Content.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);

                //跳转m3u8地址
                if (m3u8Content.IndexOf("#EXT-X-STREAM-INF") != -1)
                {
                    Url = attrs[attrs.Length - 1];
                    m3u8Content = GetM3u8Content(Url);
                    attrs = m3u8Content.Split(new char[] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
                }

                foreach (string line in attrs)
                {
                    if (string.IsNullOrEmpty(line))
                    {
                        continue;
                    }
                    if (line.StartsWith("http"))
                    {
                        Uri uri = new Uri(line);
                        if (uri.Host != new Uri(this.Url).Host) //跳过广告
                            continue;
                    }
                    if (line.Trim().StartsWith("#EXT-X-KEY"))
                    {
                        if (line.Trim().StartsWith("#EXT-X-KEY:METHOD=NONE"))
                        {
                            //this.AESKey = string.Empty;
                        }
                        else
                            this.AESKey = GetM3u8AesKey(line.Trim());
                    }
                    if (line.Trim().StartsWith("#EXT"))
                    {
                        continue;
                    }
                    TsEntity ts = new TsEntity();
                    ts.Status = 0;
                    ts.Url = line.Trim();
                    this.Parts.Add(ts);
                }
                this.CompleteNum = 0;
                this.PartNum = this.Parts.Count;
                if (string.IsNullOrEmpty(m3u8Content))
                    this.Status = 4;
                else
                    this.Status = 0;
            }
        }

        public M3u8TaskEntity(long id, string url, string name)
        {
            //this.Id = SeqKit.Next();
            this.Id = id;
            this.Name = name;
            this.Url = url;
            this.Parts = new List<TsEntity>();

            string m3u8Content = GetM3u8Content(url);
            string[] attrs = m3u8Content.Split('\n');
            foreach (string line in attrs)
            {
                if (String.IsNullOrEmpty(line))
                {
                    continue;
                }
                if (line.Trim().StartsWith("#EXT"))
                {
                    continue;
                }
                TsEntity ts = new TsEntity();
                ts.Status = 0;
                ts.Url = line.Trim();
                this.Parts.Add(ts);
            }
            this.CompleteNum = 0;
            this.PartNum = this.Parts.Count;
        }

        public string GetM3u8Content(string m3u8Url)
        {
            Console.WriteLine($"解析M3u8内容->{m3u8Url}");
            this.Url = m3u8Url;

            string m3u8Content = string.Empty;
            try
            {
                m3u8Content = HttpHelper.HttpStringDownload(m3u8Url);
            }
            catch (Exception ex)
            {
                MessageBox.Show("下载失败：" + ex.Message);
                return string.Empty;
            }

            if (m3u8Content.ToLower().StartsWith("#EXTM3U".ToLower()))
            {
                if (!m3u8Content.ToLower().Contains(".m3u8"))
                {
                    return m3u8Content;
                }
                foreach (string line in m3u8Content.Split('\n'))
                {
                    if (!line.Trim().ToLower().EndsWith(".m3u8"))
                    {
                        continue;
                    }
                    if (!line.ToLower().StartsWith("http"))
                    {
                        if (line.StartsWith("/"))
                        {
                            Uri uri = new Uri(m3u8Url);
                            this.Url = String.Format("{0}://{1}:{2}{3}", uri.Scheme, uri.Host, uri.Port, line);
                            return GetM3u8Content(this.Url);
                        }

                        this.Url = m3u8Url.Substring(0, m3u8Url.LastIndexOf("/")) + "/" + line;
                        return GetM3u8Content(this.Url);
                    }
                    return GetM3u8Content(line);
                }
            }
            m3u8Content = HttpUtility.UrlDecode(m3u8Content);
            if (!m3u8Content.ToLower().Contains(".m3u8"))
            {
                WebBrowser browser = new WebBrowser();
                browser.Url = new Uri(m3u8Url);

                browser.DocumentCompleted += (send, e) =>
                {
                    m3u8Content = browser.Document.Body.InnerHtml;
                };
            }

            int index = m3u8Content.ToLower().IndexOf(".m3u8");
            if (index < 0)
            {
                return null;
            }
            string content = m3u8Content.Substring(0, index + 5);

            int start = 0;

            string[] splits = new string[] { "\"", "\n", "\t", "    ", " ", "'" };
            foreach (string split in splits)
            {
                int temp = content.LastIndexOf(split);
                if (start < temp)
                {
                    start = temp;
                }
            }
            string url = content.Substring(start + 1);
            if (!url.ToLower().StartsWith("http"))
            {
                if (url.StartsWith("/"))
                {
                    Uri uri = new Uri(m3u8Url);
                    return GetM3u8Content(String.Format("{0}://{1}:{2}/{3}", uri.Scheme, uri.Host, uri.Port, url));
                }
                return GetM3u8Content(m3u8Url.Substring(0, m3u8Url.LastIndexOf("/")) + url);
            }
            return GetM3u8Content(url);
        }

        public string GetM3u8AesKey(string line)
        {
            try
            {
                if (line.IndexOf("URI=\"") == -1)
                    return "";

                var url = line.Substring(line.IndexOf("URI=\"") + "URI=\"".Length);
                url = url.Remove(url.IndexOf("\""));

                if (url.StartsWith("/"))
                {
                    Uri uri = new Uri(Url);
                    url = String.Format("{0}://{1}:{2}{3}", uri.Scheme, uri.Host, uri.Port, url);
                }
                else
                {
                    url = Url.Substring(0, Url.LastIndexOf("/") + 1) + url;
                }
                byte[] data = HttpHelper.HttpDownload(url);
                return Encoding.UTF8.GetString(data);
            }
            catch (Exception)
            {
                return "";
            }
        }

        public void Download()
        {
            Status = 1;
            for (int i = 0; i < SystemConfig.TaskThread; i++)
            {
                Task.Factory.StartNew(() =>
                {

                    while (Parts.Count > 0)
                    {
                        try
                        {
                            if (Status == -1)
                            {
                                break;
                            }
                            if (Status != 1)
                            {
                                Thread.Sleep(100);
                                continue;
                            }
                            foreach (TsEntity part in Parts)
                            {
                                string url = "";
                                try
                                {
                                    if (part == null)
                                        continue;

                                    lock (part)
                                    {
                                        if (part.Status != 0)
                                        {
                                            continue;
                                        }
                                        part.Status = 1;
                                    }
                                    url = part.Url;
                                    if (!url.ToLower().StartsWith("http"))
                                    {
                                        if (url.StartsWith("/"))
                                        {
                                            Uri uri = new Uri(Url);
                                            url = String.Format("{0}://{1}:{2}{3}", uri.Scheme, uri.Host, uri.Port, url);
                                        }
                                        else
                                        {
                                            url = Url.Substring(0, Url.LastIndexOf("/") + 1) + url;
                                        }
                                    }
                                    byte[] data = HttpHelper.HttpDownload(url);
                                    if (!string.IsNullOrEmpty(this.AESKey))
                                    {
                                        var aes = new AESEncoder(this.AESKey);
                                        part.Data = aes.DecryptBytes(data);
                                    }
                                    else
                                    {
                                        part.Data = data;
                                    }
                                    part.Status = 2;
                                    CompleteNum++;
                                    Console.WriteLine($"下载进度->{CompleteNum * 100D / PartNum}");
                                    MergeToFile();
                                    if (CompleteNum == PartNum)
                                        this.Status = 5;
                                    if (CompleteNum == PartNum + 1)
                                        this.Status = 5;
                                    //if (this.Parts.Count == 0)
                                    //    this.Status = 5;
                                    break;
                                }
                                catch (Exception e)
                                {
                                    Console.WriteLine($"下载失败->{e.Message}:{Url}:{url}");
                                    part.Status = 0;
                                }
                            }
                        }
                        catch { }
                    }
                }, TaskCreationOptions.LongRunning);
            }
        }

        public void ReadTsFile()
        {
            var folderPath = this.Url;
            var tsList = new List<TsEntity>();
            var dir = new DirectoryInfo(folderPath);
            var files = dir.GetFiles("*.ts").ToList();
            files = files.OrderBy(x => x.Name.Replace(".ts", "").ToInt()).ToList();

            foreach (var file in files)
            {
                var stream = file.OpenRead();
                byte[] bytes = new byte[file.Length];
                stream.Read(bytes, 0, bytes.Length);

                tsList.Add(new TsEntity
                {
                    Url = file.FullName,
                    Data = bytes,
                    Status = 2
                });
            }

            var aesKey = string.Empty;
            var aesFile = folderPath + "/0.key";
            if (File.Exists(aesFile))
            {
                aesKey = File.ReadAllText(aesFile);
            }
            this.AESKey = aesKey;
            this.Parts = tsList;
        }


        public void MergeToFile()
        {
            lock (String.Intern(Url))
            {
                if (Parts.Count < 1)
                {
                    return;
                }
                string _file = Name + ".cache";
                if (!File.Exists(_file))
                {
                    var file = File.Create(_file);
                    file.Dispose();
                    if (Parts[0].Status != 2)
                    {
                        return;
                    }
                }
                FileStream fStream = new FileStream(_file, FileMode.Append);
                while (Parts.Count > 0 && Parts[0].Status == 2)
                {
                    if (!string.IsNullOrEmpty(this.AESKey))
                    {
                        try
                        {
                            var aes = new AESEncoder(this.AESKey);
                            Parts[0].Data = aes.DecryptBytes(Parts[0].Data);
                        }
                        catch (Exception ex)
                        {
                            LogHelper.LocalSaveLog($"解密{Parts[0].Url}失败，" + ex.StackTrace);
                            //可能是广告，跳过
                            Parts.Remove(Parts[0]);
                            continue;
                        }
                    }
                    fStream.Write(Parts[0].Data, 0, Parts[0].Data.Length);
                    Parts.Remove(Parts[0]);
                }
                fStream.Close();
                if (Parts.Count == 0)
                {
                    if (File.Exists(Name))
                    {
                        var fileInfo = new FileInfo(Name);
                        Name = fileInfo.Name + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + fileInfo.Extension;
                    }
                    File.Move(Name + ".cache", Name);
                }
            }
        }
    }
}
